﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using DocumentMaking.Lib.JsonContexts;
using DocumentMaking.Lib.Models;
using DocumentMaking.Lib.Services;
using DynamicData;

namespace DocumentMaking.FluentNext.ViewModels;

public class DocumentMakingViewModel : ViewModelBase
{
    #region prop

    private SettingModel Model { get; set; } = new();

    private string? _company;

    public string? Company
    {
        get => _company;
        set => SetField(ref _company, value);
    }

    private string? _docPath = "";

    public string? DocPath
    {
        get => _docPath;
        set => SetField(ref _docPath, value);
    }

    private string? _softName = "";

    public string? SoftName
    {
        get => _softName;
        set => SetField(ref _softName, value);
    }

    public ObservableCollection<MemberModel> Members { get; set; } = [];

    public GitIgnore Ignore { get; set; } = GitIgnore.Default;
    public ObservableCollection<SourceFileModel> Files { get; set; } = [];

    #endregion

    #region Func

    public void Init(DataModel model)
    {
        Company = model.Company;
        DocPath = model.DocPath;
        SoftName = model.SoftName;
        Members.Clear();
        Members.Add(model.Members);
        Ignore = new GitIgnore(model.Ignore);
        Files.Clear();
        Files.Add(model.Files);
    }

    public void Init(SettingModel? model)
    {
        DocPath = model?.DefaultOutputPath ?? "";
        Company = model?.DefaultOrganize ?? "";
        Model = model ?? new SettingModel();
    }

    public void Clear()
    {
        Company = Model.DefaultOrganize;
        SoftName = "";
        DocPath = Model.DefaultOutputPath;
        Members.Clear();
        Ignore = new GitIgnore(Array.Empty<string>());
        Files.Clear();
    }

    public async void Choose()
    {
        var result = await new Window().StorageProvider
            .OpenFolderPickerAsync(new FolderPickerOpenOptions());
        if (result.Count == 0)
            return;
        DocPath = result[0].Path.LocalPath;
    }

    #region AddOther

    public async Task<bool> AddIgnore()
    {
        var a = await new Window().StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions());
        if (a.Count == 0) return false;
        Ignore = new GitIgnore(await File.ReadAllLinesAsync(a[0].Path.LocalPath));
        return true;
    }

    #endregion

    #region Remove

    public void RemoveMember(MemberModel member)
    {
        Members.Remove(member);
    }

    public void Remove(SourceFileModel removeModel)
    {
        Files.Remove(removeModel);
    }

    #endregion

    #region AddFile

    public async void Add()
    {
        var result =
            await new Window().StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions()
                {
                    AllowMultiple = true
                }
            );
        if (result.Count == 0) return;
        foreach (var storageFile in result)
        {
            var file = new SourceFileModel(storageFile.Path.LocalPath);
            if (file.IsCodeFile)
                Files.Add(file);
        }
    }

    public async void AddFromDirectory()
    {
        var result = await new Window().StorageProvider
            .OpenFolderPickerAsync(new FolderPickerOpenOptions());
        if (result.Count == 0) return;
        if (!Directory.Exists(result[0].Path.LocalPath)) return;
        SearchAndAddCodeFileFromPath(new DirectoryInfo(result[0].Path.LocalPath));
    }

    private void SearchAndAddCodeFileFromPath(DirectoryInfo dir)
    {
        foreach (var info in dir.EnumerateFileSystemInfos())
        {
            if (Ignore.IsIgnored(info.FullName)) continue;
            switch (info)
            {
                case DirectoryInfo directoryInfo:
                {
                    SearchAndAddCodeFileFromPath(directoryInfo);
                    continue;
                }
                case FileInfo fileInfo:
                {
                    var a = new SourceFileModel(fileInfo.FullName);
                    if (a.IsCodeFile) Files.Add(a);
                    continue;
                }
            }
        }
    }

    #endregion

    #endregion

    public async Task<string> Done()
    {
        if (Files.Count == 0 || string.IsNullOrEmpty(DocPath) ||
            string.IsNullOrEmpty(SoftName) || string.IsNullOrEmpty(Company))
            return "信息未填完";

        var model = new DataModel()
        {
            Company = Company, DocPath = DocPath, SoftName = SoftName, Members = Members.ToArray(),
            Files = Files.ToArray(), Ignore = Ignore.IgnoreList
        };

        var lineNum = 0;
        try
        {
            DocPath = Path.Combine(DocPath, SoftName);
            if (!Directory.Exists(DocPath))
                Directory.CreateDirectory(DocPath);
            await File.WriteAllTextAsync(Path.Combine(DocPath, $"{SoftName}.json"),
                JsonSerializer.Serialize(model, typeof(DataModel), InfoContext.Default));

            //生成代码文件
            var path = Path.Combine(DocPath, $"程序-{SoftName}.docx");
            if (File.Exists(path)) File.Delete(path);
            var service = new WordDocumentService(path);
            // 同步方法，源代码有序，但性能较差
            foreach (var item in Files)
            {
                var encoding = Encoding.UTF8;
                // 部分IDE对cpp文件按照gbk编码
                if (item.Language is "C++") encoding = Encoding.GetEncoding("GB2312");

                using var reader = new StreamReader(item.FilePath, encoding);

                while (await reader.ReadLineAsync() is { } line)
                {
                    if (string.IsNullOrEmpty(line)) continue;
                    while (line.StartsWith(' '))
                        line = line.TrimStart(' ');
                    while (line.StartsWith('\t'))
                        line = line.TrimStart('\t');
                    service.AppendText(line);
                    lineNum++;
                }
            }

            service.Save(SoftName, Company);
            service.Dispose();

            //生成合同
            if (Members.Count != 0)
            {
                var contract = new WordDocumentService(Path.Combine(DocPath, "合同.docx"));
                contract.BuildAndSaveContract(Members.ToList(), SoftName);
                contract.Dispose();   
            }
        }
        catch (Exception e)
        {
            return e.Message;
        }

        File.Create(Path.Combine(DocPath, $"代码行数:{lineNum}.md"));
        
        return $"成功! 代码文件的行数为:{lineNum}";
    }
}